package com.zm.shop.controller;

import com.alibaba.fastjson.JSON;
import com.zm.shop.feign.GoodsService;
import com.zm.shop.common.entity.GoodsEntity;
import com.zm.shop.common.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Controller
@Slf4j
@RequestMapping("/item")
public class ItemController {
    private String ITEM_REDIS="shop:item:";
    private String ITEM_LOCK_REDIS="shop:lock:item";

    private StringRedisTemplate redisTemplate;
    private GoodsService goodsService;

    @GetMapping("/{id}.html")
    public String page(@PathVariable Integer id, ModelMap modelMap){
        log.debug("id:",id);
        //从redis查询商品
        String goods = getGoods(id);
        if ("null".equals(goods)){ //解决缓存穿透 null为默认值
            throw new BusinessException("该商品不存在", 102);
        }
        //JSON数据转对象
        GoodsEntity goodsEntity = JSON.parseObject(goods, GoodsEntity.class);
        modelMap.put("goodsEntity",goodsEntity);
        return "item";
    }



//    public String getGoods(Integer id){
//        //从redis查询KEY 获得商品对象
//        String goods = redisTemplate.opsForValue().get(ITEM_REDIS + id);
//
//        if (StringUtils.isEmpty(goods)){ //redis查询为空 需要去数据库查询 加锁防止缓存击穿
//            String uuid= UUID.randomUUID().toString();
//
//
//            //获取锁
//            try {
//                Boolean lock = redisTemplate.opsForValue().setIfAbsent(ITEM_LOCK_REDIS, uuid, 1, TimeUnit.SECONDS);
//                if (lock){ //拿到锁了
//                    //去数据库查询
//                    GoodsEntity goodsEntity = goodsService.getGoodsEntityById(id);
//                    if (goodsEntity!=null) {  //数据库查询不为空添加到redis，为空给默认值
//                        //添加到缓存
//                        goods = JSON.toJSONString(goodsEntity);
//                        redisTemplate.opsForValue().set(ITEM_REDIS + id, goods, 7, TimeUnit.DAYS);
//                    }else {
//                        goods="null"; //查询结果给默认值，解决缓存穿透
//                        redisTemplate.opsForValue().set(ITEM_REDIS + id, goods, 7, TimeUnit.DAYS);
//                    }
//                }else { //没拿到锁  等待一会然后重新获取锁
//                    try {
//                        Thread.sleep(300);
//                        getGoods(id);
//                    } catch (InterruptedException e) {
//                        e.printStackTrace();
//                    }
//                }
//            } finally {  //释放锁
//                String lunaScript = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
//                        "then\n" +
//                        "    return redis.call(\"del\",KEYS[1])\n" +
//                        "else\n" +
//                        "    return 0\n" +
//                        "end";
//                // 使用luna脚本释放锁，因为luna脚本是原子性的
//                DefaultRedisScript defaultRedisScript = new DefaultRedisScript(lunaScript, Long.class);
//                redisTemplate.execute(defaultRedisScript, Arrays.asList(ITEM_LOCK_REDIS), uuid);
//            }
//        }
//
//        return goods;
//    }

    //使用redisson实现分布式锁
    @Autowired
    private RedissonClient redissonClient;
    public String getGoods(Integer id){
        //从redis里查询
        String goods = redisTemplate.opsForValue().get(ITEM_REDIS + id);
        //创建锁
        RLock rLock = redissonClient.getLock(ITEM_LOCK_REDIS);
        if (StringUtils.isEmpty(goods)){
            Boolean lock=false;
            try {
                // 加锁，加锁失败等待1s就返回 ，成功就1s的生存时间
                lock = rLock.tryLock(1, 1, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                e.printStackTrace();
                log.error("加锁失败", e);
            }
            if (lock){
                try {
                    //从数据库查询
                    GoodsEntity goodsEntity = goodsService.getGoodsEntityById(id);
                    if (goodsEntity!=null){ //存入redis里
                        goods= JSON.toJSONString(goodsEntity);
                    }else {
                        goods="null";
                    }
                        redisTemplate.opsForValue().set(ITEM_REDIS+id,goods,7,TimeUnit.DAYS);
                } finally { //释放锁
                    try {
                        Thread.sleep(200);
                        rLock.unlock();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }else { //重新获取锁
                getGoods(id);
            }

        }
        return goods;
    }

}
